package com.cellulam.framework.push.smtp;

import com.cellulam.framework.push.common.BaseMailer;
import com.cellulam.framework.push.common.ContentMail;
import com.cellulam.framework.push.common.MailCallback;
import com.cellulam.framework.push.common.MailException;
import com.cellulam.framework.push.mail.MailProperties;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.mail.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.mail.internet.MimeUtility;
import java.io.File;
import java.util.List;

/**
 * 线程安全
 */
public class SmtpMailer extends BaseMailer {

    private static final Logger log = LoggerFactory.getLogger(SmtpMailer.class);

    private String mailServer;
    private Integer smtpPort;
    private String authUsername;
    private String authPassword;

    public SmtpMailer(MailProperties properties) {
        this(properties.getSmtpHost(), properties.getSmtpPort(), properties.getSmtpUsername(), properties.getSmtpPassword());

        log.info("SMTP mail with host#{}", mailServer);
    }

    public SmtpMailer(String mailServer, String authUsername, String authPassword) {
        this(mailServer, 25, authUsername, authPassword);
    }

    public SmtpMailer(String mailServer, Integer smtpPort, String authUsername, String authPassword) {
        this.mailServer = mailServer;
        this.smtpPort = smtpPort;
        this.authUsername = authUsername;
        this.authPassword = authPassword;

        if (StringUtils.isEmpty(mailServer)) {
            throw new MailException("邮件服务器地址[mail.smtp.host]不能为空");
        }
        if (StringUtils.isEmpty(authUsername)) {
            throw new MailException("邮件服务用户[mail.auth.username]不能为空");
        }
        if (StringUtils.isEmpty(authPassword)) {
            throw new MailException("邮件服务密码[mail.auth.password]不能为空");
        }

        log.info("SMTP mail with host#{}", mailServer);
    }

    /**
     * @param mail
     * @return message id
     * @throws EmailException
     */
    public String send(ContentMail mail) {
        if (mail.isText()) {
            return sendText(mail);
        } else if (mail.isHtml()) {
            return sendHtml(mail);
        } else if (mail.isMixed()) {
            return sendMixed(mail);
        } else {
            throw new MailException("unknown mail type[" + mail.getType() + "]");
        }
    }

    private String sendText(ContentMail mail) {
        SimpleEmail email = new SimpleEmail();
        setEmail(email, mail.getSubject(), mail.getContent(), mail.getTos());
        return executeSend(email, mail.isAsync(), mail.getMailCallback());
    }

    private String sendHtml(ContentMail mail) {
        HtmlEmail email = new HtmlEmail();
        setEmail(email, mail.getSubject(), mail.getContent(), mail.getTos());
        return executeSend(email, mail.isAsync(), mail.getMailCallback());
    }

    private String sendMixed(ContentMail mail) {
        try {
            HtmlEmail email = new HtmlEmail();
            setEmail(email, mail.getSubject(), mail.getContent(), mail.getTos());
            for (File f : mail.getAttachments()) {
                EmailAttachment attachment = new EmailAttachment();
                attachment.setPath(f.getAbsolutePath());
                attachment.setDisposition(EmailAttachment.ATTACHMENT);
                attachment.setDescription(f.getName());
//                attachment.setName(f.getName());
                // 使用Base64的方式转码文件名. 默认是Q(quoted-printable), 在windows的outlook下, 不能解析为中文
                attachment.setName(MimeUtility.encodeText(f.getName(), "UTF-8", "B"));

                email.attach(attachment);
            }
            return executeSend(email, mail.isAsync(), mail.getMailCallback());
        } catch (Exception e) {
            throw new MailException("sendMixed error: " + e.toString(), e);
        }
    }

    /**
     * message id
     *
     * @param email
     * @param async
     * @return
     * @throws EmailException
     */
    private String executeSend(final Email email, boolean async, MailCallback callback) {
        try {
            String messageId = "";
            if (async) {
                pool.execute(() -> {
                    try {
                        String mId = email.send();
                        log.info("async send mail with message-id#{}", mId);
                        if (callback.getSendSuccess() != null) {
                            callback.getSendSuccess().accept(mId);
                        }
                    } catch (EmailException e) {
                        log.error("email.send error", e);
                        if (callback.getSendFail() != null) {
                            callback.getSendFail().accept(e);
                        }
                    }
                });
            } else {
//                email.ssl
                messageId = email.send();
                log.debug("after send email, message id is " + messageId);
                if (callback.getSendSuccess() != null) {
                    callback.getSendSuccess().accept(messageId);
                }
            }

            return messageId;
        } catch (Exception e) {
            if (callback.getSendFail() != null) {
                callback.getSendFail().accept(e);
            }
            throw new MailException("executeSend error", e);
        }
    }

    private void setEmail(Email email, String subject, String content, List<String> tos) {
        try {
            email.setHostName(mailServer);
            email.setSmtpPort(smtpPort);
            email.setSSLOnConnect(false);
            email.setAuthentication(authUsername, authPassword);
            email.setCharset("UTF-8");// 必须放在前面，否则乱码
            email.setFrom(authUsername);

            for (String to : tos) {
                if (StringUtils.isNotEmpty(to)) {
                    email.addTo(to);
                }
            }
            email.setSubject(subject);
            email.setMsg(content);
        } catch (Exception e) {
            throw new MailException("setEmail error", e);
        }
    }
}
